/*****************************************************************************
*    Ali Corp. All Rights Reserved. 2013 Copyright (C)
*
*    File:    bootloader_vmx.c
*
*    Description:    This file contains all globe micros and functions declare
*                    of Flash boot loader for VMX.
*    History:
*           Date            Athor        Version          Reason
*       ============    =============   =========   =================
*   1.  July.16.2013     Ryan.Chen       Ver 0.1    Create file.
*****************************************************************************/

#include "bootloader.h"
#include <bus/i2c/i2c.h>
#include <api/libcas/vmx/bc_types.h>

#define SIGNATURE_SIZE 256
#define MAIN_OK			0x1
#define SEE_OK			0x2
#define OTA_LOADER_OK	0x4

extern UINT8 *g_see_unziped;
extern UINT32 unzip_length;
extern UINT32 g_ota_offset;
extern void __SEE_ROM_START();
extern void __SEE_ROM_END();

//extern struct pan_device *panel_dev;


UINT32 g_boot_type = 0;             /* 0--Nor, 1--Nand, 2--SDIO, 3--EMMC, 9--Unknow */
UINT32 pos = 0;

void AppInit(void)
{
	UINT8  i;    
	UINT32 chid;    
	UINT32 offset;
	UINT32 cpu_mc_len=0, see_mc_len=0, ota_len = 0;
	UINT8 *main_entry,*see_entry,*see_ram_address;
	static UINT8 *main_encrypted = NULL;
	static UINT8 *main_decrypted = NULL;
	static UINT8 *see_decrypted = NULL;
	UINT32 block_addr = 0, block_len = 0;
	UINT8  boot_type;
	UINT32 boot_code,tmp,data;
	UINT8 *temp_buffer = NULL;
	UINT8 sys_sw_wrong = 0;
	CHUNK_HEADER chunk_hdr;

	UINT32 fail_type = 0;

	static UINT8 *ota_decrypted = NULL;
	RET_CODE ret=RET_SUCCESS;
	UINT8 verify_result=0;

#ifdef BL_VMX_AS
	uint8_t* signature;
	uint8_t* tmp_buffer;
	uint32_t len, maxLen;
	uint8_t mode, errorCode;
	UINT8 *main_block, *see_block;
	unsigned int header_crc=0;
#endif

#ifdef BL_VMX_STD	
	struct sto_device *test_flash_dev = NULL;				
	UINT8 *temp1,*temp2,*temp3,*temp4,*temp5,*temp6,errorCode,chkret=0;
	UINT32 mpos=0,spos=0;		
#endif

	sys_uart_init();
	/*bl_see boot*/
	temp_buffer = (UINT8*)(0xFFFFFFF8&(UINT32)MALLOC(__SEE_ROM_END - __SEE_ROM_START+0xf));    

	MEMCPY((UINT8 *)(temp_buffer), __SEE_ROM_START, (__SEE_ROM_END - __SEE_ROM_START));
	osal_cache_flush(temp_buffer,(__SEE_ROM_END - __SEE_ROM_START));

	self_boot(SEE_ENTRY+0x1000000, temp_buffer, (__SEE_ROM_END - __SEE_ROM_START), SEE_ENTRY+0x1000000);
	FREE(temp_buffer);

	g_boot_type = sys_ic_get_boot_type();
	FIXED_PRINTF("g_boot_type = %d\n", g_boot_type);

	if(g_boot_type != 1)
	{
		boot_code = *(UINT32 *)(0xA0000110);
		/* Cold bootup */
		boot_type = *(UINT8 *)(SYS_FLASH_BASE_ADDR + HW_SET_BOOTTYPE);
		if ((boot_type & BOOT_TYPE_MASK) && boot_code == 0xdead3bee)
		{
			/* If cold bootup and enter standby enabled, enter standby */
			sys_ic_enter_standby(0, 0);
		}
	}
	else
	{
		/*transfer S5 and S7 to 220 for linux reboot get timer*/
		//if(boot_type == 0xdead1bee)
		{
		*(UINT32 *)(0xA0000220) = *(UINT32 *)(0xA0000110);
		*(UINT32 *)(0xA0000224) = *(UINT32 *)(0xA0000114);
		*(UINT32 *)(0xA0000228) = *(UINT32 *)(0xA0000118);
		}
	}

	//sys_uart_init();

	/* Init front panel and IR */
	data = *((volatile unsigned long *)0xb8000440);
	data |= (1 << (134-128));  // GPIO134: panel and scart i2c gpio
	data |= (1 << (135-128));  // GPIO135: panel and scart i2c gpio
	*((unsigned long *)0xb8000440) = data;

	//init i2c
	i2c_scb_attach(3);
	i2c_gpio_attach(1);
	i2c_mode_set(I2C_TYPE_GPIO0/*I2C_TYPE_SCB1*/, 100000, 1);		// SCART/panel

	sys_panel_init();
    panel_dev = (struct pan_device *)dev_get_by_id(HLD_DEV_TYPE_PAN, 0);
    pan_display(panel_dev, " ON ", 4);
	FIXED_PRINTF("sys_panel_init\n");
	bl_init_flash();
	FIXED_PRINTF("bl_init_flash\n");

	dsc_api_attach();
	ce_api_attach();	
	see_step1();

#ifdef BL_VMX_STD
	test_flash_dev = (struct sto_device *)dev_get_by_type(NULL, HLD_DEV_TYPE_STO);
#endif

	MG_Setup_CRC_Table();	
	if(sto_chunk_check())
	{
		FIXED_PRINTF("firmware chunklist error.\n");
		sys_sw_wrong = 1;
	}

#ifdef ENABLE_UART_UPG	
	if( TRUE == check_sw_upgrade() ) /* force download */	
	{
		serial_upgrade();
	}
#endif

	if(RET_SUCCESS != bl_prepare_key(&pos))        
	{
		FIXED_PRINTF("prepare key fail!\n");
		goto bl_reboot;
	}

	if(sys_sw_wrong != 1)
	{
		FIXED_PRINTF("main code\n");
		
#ifdef BL_VMX_STD	//decrypt Main code

		//	1. get flash dev
		if (test_flash_dev == NULL)
		{
			FIXED_PRINTF("Fail to find flash device!\n");
			sys_sw_wrong =1;
		}
		// 2. [Main chunk]get Chunk Hdr, malloc buf, and get chunk data 
		chid = CHUNKID_MAINCODE;
		if(0==sto_get_chunk_header(CHUNKID_MAINCODE, &chunk_hdr))
		{
			FIXED_PRINTF("Fail to get Chunk Header of ID(0x%x)!\n",CHUNKID_MAINCODE);
			sys_sw_wrong =1;
		}

		// reset watch dog timer
		*(UINT32 *)(0xb8018500) = 0;
				
		mpos=(UINT32)sto_chunk_goto(&chid, CHUNKID_MAINCODE_MASK, 1);
		temp1 = ( UINT8* ) (  0xFFFFFFF8 & ( UINT32 ) MALLOC ( chunk_hdr.offset+ 0xf ) );						
		if(NULL!=temp1)
		{
			MEMSET(temp1,0xFF,chunk_hdr.offset+ 0xf);
		}
		temp2 = ( UINT8* ) (  0xFFFFFFF8 & ( UINT32 ) MALLOC ( chunk_hdr.offset+ 0xf ) );						
		
		if(NULL!=temp2)
		{
			MEMSET(temp2,0xFF,chunk_hdr.offset+ 0xf);
		}
		sto_get_data(test_flash_dev, temp1, mpos, chunk_hdr.offset);
	
		// 3. call verifySignature_std
		chkret=verifySignature_std(temp1, temp2, chunk_hdr, &errorCode);
		FIXED_PRINTF("%s-%d:chkret(%d),errorcode(%d)\n",__FILE__,__LINE__,chkret,errorCode);
		if(0==chkret)
		{
			switch(errorCode)
			{
				case 0:
					break;
				case 1:
					sys_sw_wrong =1;	
					break;
				case 2:
					update_flash_data(mpos, chunk_hdr.offset, temp1);
					FIXED_PRINTF("Finish write back to Main Chunk!");
					break;
				case 3:
					do{} while(1);
					break;
				default:break;	
			}
		}
		else if(1==chkret)
		{
			main_decrypted=temp2+CHUNK_HEADER_SIZE;
			cpu_mc_len=chunk_hdr.len-CHUNK_HEADER_SIZE + CHUNK_NAME;
		}
		else
		{
			sys_sw_wrong=1;
		}
#elif defined(BL_VMX_AS) /* BL_VMX_AS */
		/* main sw verify */
		chid=CHUNKID_MAINCODE;
		block_addr=sto_chunk_goto(&chid, CHUNKID_MAINCODE_MASK, 1);
		if(ERR_FAILUE == block_addr)
		{
			FIXED_PRINTF("FTA: Can't find chunk id 0x%08x.\n", CHUNKID_MAINCODE);
			sys_sw_wrong = 1;
			goto bl_ota;	//can not find main, enter ota loader
		}
		else
		{
			// reset watch dog timer
			*(UINT32 *)(0xb8018500) = 0;

			block_len=sto_fetch_long((UINT32)block_addr + CHUNK_OFFSET);
			main_block = (UINT8*)( (0xFFFFFFF8&(UINT32)MALLOC(block_len+0xf)) );
			cpu_mc_len = sto_fetch_long((UINT32)block_addr + CHUNK_LENGTH);
			FIXED_PRINTF("Main code block_addr=0x%x,block_len=0x%x,cpu_mc_len=0x%x\n", block_addr,block_len,cpu_mc_len);
			sto_get_data(flash_dev, (void *)main_block, block_addr, block_len);
		
			main_decrypted=main_block+CHUNK_HEADER_SIZE;
		
			tmp_buffer = (UINT8*)( (0xFFFFFFF8&(UINT32)MALLOC(block_len+0xf)) );
			len=cpu_mc_len-(CHUNK_HEADER_SIZE-CHUNK_NAME)-SIGNATURE_SIZE;
			signature=main_decrypted+len;
			maxLen=len;
			mode=0; 	//from flash
			ret=verifySignature(signature, main_decrypted, tmp_buffer, len, maxLen, mode,&errorCode);
			FREE(tmp_buffer);
			tmp_buffer=NULL;
			if(ret==1)	//app can be started
			{
				verify_result|=MAIN_OK;
				FIXED_PRINTF("Main code verify ok\n");
			}
			else	// don't start app
			{
				if(errorCode==2)	//store app to flash and reboot later
				{
					//calculate crc value for chunk header
					header_crc=MG_Table_Driven_CRC(0xFFFFFFFF, main_block+CHUNK_NAME, cpu_mc_len);
					*(main_block+CHUNK_CRC)=(UINT8)(header_crc>>24);
					*(main_block+CHUNK_CRC+1)=(UINT8)(header_crc>>16);
					*(main_block+CHUNK_CRC+2)=(UINT8)(header_crc>>8);
					*(main_block+CHUNK_CRC+3)=(UINT8)(header_crc);
					//memcpy(main_block+CHUNK_CRC,&header_crc,4);
					FIXED_PRINTF("Main block header_crc=0x%x first byte=0x%x\n",header_crc,*(main_block+CHUNK_CRC));
					update_flash_data(block_addr, block_len, main_block);
					
					FIXED_PRINTF("Main code verify ok and re-encrypt\n");
				}
				else if(errorCode==1)
				{
					FIXED_PRINTF("Main code verify failed\n");
				}
			}
		}
#endif

		FIXED_PRINTF("see code\n");
#ifdef BL_VMX_STD	//decrypt See code
		// 2. [See chunk]get Chunk Hdr, malloc buf, and get chunk data
		chid = CHUNKID_SEECODE;
		if(0==sto_get_chunk_header(CHUNKID_SEECODE, &chunk_hdr))
		{
			FIXED_PRINTF("Fail to get Chunk Header of ID(0x%x)!\n",CHUNKID_SEECODE);
			sys_sw_wrong =1;
		}
		spos=(UINT32)sto_chunk_goto(&chid, CHUNKID_SEECODE_MASK, 1);
		temp3 = ( UINT8* )	( 0xFFFFFFF8 & ( UINT32 ) MALLOC ( chunk_hdr.offset+ 0xf ));						

		if(NULL!=temp3)
		{
			MEMSET(temp3,0xFF,chunk_hdr.offset+ 0xf);
		}
		temp4 = ( UINT8* ) ( 0xFFFFFFF8 & ( UINT32 ) MALLOC ( chunk_hdr.offset+ 0xf )); 					

		if(NULL!=temp4)
		{
			MEMSET(temp4,0xFF,chunk_hdr.offset+ 0xf);
		}
		sto_get_data(test_flash_dev, temp3, spos, chunk_hdr.offset);
		FIXED_PRINTF("see: 0x%2x %2x %2x %2x",temp3[0],temp3[1],temp3[2],temp3[3]);
		// 3. call verifySignature_std
		chkret=verifySignature_std(temp3, temp4, chunk_hdr, &errorCode);
		FIXED_PRINTF("%s-%d:chkret(%d),errorcode(%d)\n",__FILE__,__LINE__,chkret,errorCode);
		if(0==chkret)
		{
			switch(errorCode)
			{
				case 0:
					break;
				case 1:
					sys_sw_wrong =1;	
					break;
				case 2:
					update_flash_data(spos, chunk_hdr.offset, temp3);									
					FIXED_PRINTF("Finish write back to See Chunk!");
					goto bl_reboot;
					break;
				case 3:
					goto bl_reboot;
					break;
				default:break;	
			}
		}
		else if(1==chkret)
		{
			see_decrypted=temp4+CHUNK_HEADER_SIZE;
			see_mc_len=chunk_hdr.len-CHUNK_HEADER_SIZE + CHUNK_NAME;
			//for decompress use.
			g_see_unziped = ( UINT8* ) (  ( 0xFFFFFFF8 & ( UINT32 ) MALLOC ( see_mc_len * 4 ) ) );						
		}
		else
		{
			sys_sw_wrong=1;
		}
#elif defined(BL_VMX_AS) /* BL_VMX_AS */
		/* see sw verify */
		chid=CHUNKID_SEECODE;
	   	block_addr=sto_chunk_goto(&chid, CHUNKID_SEECODE_MASK, 1);
	   	if(ERR_FAILUE == block_addr)
	   	{
			FIXED_PRINTF("Can't find chunk id 0x%08x.\n", CHUNKID_SEECODE);
			sys_sw_wrong = 1;
			goto bl_ota;	//can not find see, enter ota loader
		}
		else
		{
			block_len=sto_fetch_long((UINT32)block_addr + CHUNK_OFFSET);
			see_block = (UINT8*)( (0xFFFFFFF8&(UINT32)MALLOC(block_len+0xf)) );
			see_mc_len = sto_fetch_long((UINT32)block_addr + CHUNK_LENGTH);
			FIXED_PRINTF("See code block_addr=0x%x,block_len=0x%x,see_mc_len=0x%x\n", block_addr,block_len,see_mc_len);
			sto_get_data(flash_dev, (void *)see_block, block_addr, block_len);

			see_decrypted=see_block+CHUNK_HEADER_SIZE;
			
			tmp_buffer = (UINT8*)( (0xFFFFFFF8&(UINT32)MALLOC(block_len+0xf)) );
			len=see_mc_len-(CHUNK_HEADER_SIZE-CHUNK_NAME)-SIGNATURE_SIZE;
			signature=see_decrypted+len;
			maxLen=len;
			mode=0;		//from flash
			ret=verifySignature(signature, see_decrypted, tmp_buffer, len, maxLen, mode,&errorCode);
			FREE(tmp_buffer);
			tmp_buffer=NULL;
			if(ret==1)	//app can be started
			{
				verify_result|=SEE_OK;
				FIXED_PRINTF("See code verify ok\n");
				g_see_unziped = ( UINT8* ) ( ( 0xFFFFFFF8 & ( UINT32 ) MALLOC ( block_len * 4 ) ) );
			}
			else	// don't start app
			{
				if(errorCode==2)	//store app to flash and reboot later
				{
					//calculate crc value for chunk header
					header_crc=MG_Table_Driven_CRC(0xFFFFFFFF, see_block+CHUNK_NAME, see_mc_len);
					*(see_block+CHUNK_CRC)=(UINT8)(header_crc>>24);
					*(see_block+CHUNK_CRC+1)=(UINT8)(header_crc>>16);
					*(see_block+CHUNK_CRC+2)=(UINT8)(header_crc>>8);
					*(see_block+CHUNK_CRC+3)=(UINT8)(header_crc);
					//memcpy(see_block+CHUNK_CRC,&header_crc,4);
					FIXED_PRINTF("See block header_crc=0x%x first byte=0x%x\n",header_crc,*(see_block+CHUNK_CRC));
					update_flash_data(block_addr, block_len, see_block);
					
					FIXED_PRINTF("See code verify ok and re-encrypt\n");
				}
				else if(errorCode==1)
				{
					FIXED_PRINTF("See code verify failed\n");
				}
			}
		}
#endif
	}

bl_ota:
#ifdef _OTA_E_
	if( sys_sw_wrong)	
	{
#ifdef BL_VMX_STD
		 MG_Setup_CRC_Table();
		 if ( 0 != find_second_loader() )
		 {			  
			 block_addr = g_ota_offset ;
			 chunk_hdr.id= sto_fetch_long((UINT32)block_addr + CHUNK_ID);
			 chunk_hdr.len= sto_fetch_long((UINT32)block_addr + CHUNK_LENGTH);
			 chunk_hdr.offset=sto_fetch_long((UINT32)block_addr + CHUNK_OFFSET);	 
			 chunk_hdr.crc=sto_fetch_long((UINT32)block_addr + CHUNK_CRC);	 
			 temp5 = (UINT8*)(0xFFFFFFF8&(UINT32)MALLOC(chunk_hdr.offset+0xf)); 	 
			 temp6 = (UINT8*)(0xFFFFFFF8&(UINT32)MALLOC(chunk_hdr.offset+0xf)); 				 
		
			 sto_get_data(flash_dev, (void *)temp5, block_addr , chunk_hdr.offset);
			 FIXED_PRINTF ( "ota info(hex): id(%x),len(%x),offset(%x),crc(%x)\n", 
				 chunk_hdr.id,chunk_hdr.len,chunk_hdr.offset,chunk_hdr.crc);
			 FIXED_PRINTF ( "Verify OTA data!\n" );
			 ret=verifySignature_std(temp5,temp6, chunk_hdr, &errorCode);
			 if(ret==1)  //app can be started
			 {
				 ota_decrypted=temp6+CHUNK_HEADER_SIZE;
				 ota_len=chunk_hdr.len-CHUNK_HEADER_SIZE + CHUNK_NAME;
				 FIXED_PRINTF("ota loader verify ok\n");
			 }
			 else	 // don't start app
			 {
				 if(errorCode==2)
				 {
					 FIXED_PRINTF("ota loader should not enter here\n");
				 }
				 else if(errorCode==1)
				 {
					 FIXED_PRINTF("ota loader verify failed\n");
				 }
				 FIXED_PRINTF("verify ota loder error: ret=%d\n", ret);
				 pan_display ( panel_dev, "ERR3", 4 );
				 goto bl_reboot;
			 }									 
		}
		 else
		 {
			 FIXED_PRINTF("Cannot found ota loader\n");
			 pan_display ( panel_dev, "ERR1", 4 );
			 goto bl_reboot;
		 }
#elif defined(BL_VMX_AS)
		if(0 != find_second_loader())
		{
			block_addr = g_ota_offset;
			block_len=sto_fetch_long((UINT32)block_addr + CHUNK_OFFSET);

			ota_decrypted = (UINT8*)( (0xFFFFFFF8&(UINT32)MALLOC(block_len+0xf)) );
			ota_len = sto_fetch_long((UINT32)block_addr + CHUNK_LENGTH);
			FIXED_PRINTF("Ota loader block_addr=0x%x,block_len=0x%x,ota_len=0x%x\n", block_addr,block_len,ota_len);
			sto_get_data(flash_dev, (void *)ota_decrypted, block_addr + CHUNK_HEADER_SIZE, ota_len);

			tmp_buffer = (UINT8*)( (0xFFFFFFF8&(UINT32)MALLOC(block_len+0xf)) );
			len=ota_len-(CHUNK_HEADER_SIZE-CHUNK_NAME)-SIGNATURE_SIZE;
			signature=ota_decrypted+len;
			//maxLen=block_len; //Clarify 20120806
			maxLen=len;
			mode=0; 	//from flash
			ret=verifySignature(signature, ota_decrypted, tmp_buffer, len, maxLen, mode,&errorCode);
			FREE(tmp_buffer);
			tmp_buffer=NULL;
			if(ret==1)	//app can be started
			{
				verify_result|=OTA_LOADER_OK;
				FIXED_PRINTF("ota loader verify ok\n");
			}
			else	// don't start app
			{
				if(errorCode==2)
				{
					FIXED_PRINTF("ota loader should not enter here\n");
				}
				else if(errorCode==1)
				{
					FIXED_PRINTF("ota loader verify failed\n");
				}
			}

			if((verify_result&OTA_LOADER_OK) != OTA_LOADER_OK)
			{
				FIXED_PRINTF("verify ota loder error: verify_result=%d\n", verify_result);
				goto bl_reboot;
			}
		}
		else
		{
			FIXED_PRINTF("Cannot found ota loader\n");
			pan_display(panel_dev, "Err ", 4);
			goto bl_reboot;
		}
#endif
		bl_enter_ota_loader(ota_decrypted, ota_len);  //if successful, never return
		FIXED_PRINTF("Enter ota loader fail\n");
		goto bl_reboot;
	}
#endif // _OTA_E_

#ifdef BL_VMX_AS
	if((verify_result&(MAIN_OK|SEE_OK)) != (MAIN_OK|SEE_OK))
	{
		FIXED_PRINTF("Main and See code verify_result=%d\n", verify_result);
		goto bl_reboot;
	}
#endif

	//enter system software and never return
	bl_enter_sys_sw(see_decrypted, see_mc_len,main_decrypted,cpu_mc_len);
	
bl_reboot:		
	FIXED_PRINTF("Bootloader reboot\n");  
	hw_watchdog_reboot();
}


